import {
    _decorator,
    Component,
    Node,
    resources,
    error,
    Prefab,
    find,
    SpriteFrame,
    instantiate,
    AssetManager,
    JsonAsset,
    TextAsset,
    Sprite,
    isValid
} from 'cc';

const {ccclass, property} = _decorator;

/**
 * 资源加载工具类
 */
export class ResourceUtils {
    /**
     * 加载文件夹下所有资源
     *
     * @param dir 文件夹路径
     * @param type 资源类型
     * @param onComplete 完成回调
     */
    public static loadDir<T>(dir: string, type: any,
                             onComplete: ((err: Error | null, data: T[]) => void) | null) {
        resources.loadDir(dir, type, (err: Error, data: any) => {
            if (err) {
                error("文件夹资源加载失败：", err.message || err);
                onComplete(err, data);
                return;
            }

            onComplete(null, data);
        });
    }

    /**
     * 加载资源
     *
     * @param path 资源路径
     * @param type 资源类型
     * @param onComplete 加载完成时的回调
     */
    public static loadRes<T>(path: string, type: any, onComplete: (err: Error, res: T) => void) {
        resources.load(path, type, (err: Error, res: any) => {
            if (err) {
                error("资源加载失败：", err.message || err);
                onComplete(err, res);
                return;
            }

            onComplete(null, res);
        });
    }

    /**
     * 加载特效
     *
     * @param effectPath 特效路径
     */
    public static loadEffect(effectPath: string): Promise<Prefab> {
        return new Promise<Prefab>((resolve, reject) => {
            this.loadRes<Prefab>(`prefab/effect/${effectPath}`,
                Prefab, (err: Error, res: Prefab) => {
                    if (err) {
                        error("特效加载失败：", err.message || err);
                        reject();
                        return;
                    }

                    resolve(res);
                }
            );
        });
    }

    /**
     * 加载模型
     *
     * @param modelPath 模型路径
     */
    public static loadModel(modelPath: string): Promise<Prefab> {
        return new Promise<Prefab>((resolve, reject) => {
            this.loadRes<Prefab>(`prefab/model/${modelPath}`,
                Prefab, (err: Error, res: Prefab) => {
                    if (err) {
                        error("模型加载失败：", err.message || err);
                        reject();
                        return;
                    }

                    resolve(res);
                }
            );
        });
    }

    /**
     * 加载UI模型
     *
     * @param path UI模型路径
     */
    public static loadUIPrefab(path: string): Promise<Prefab> {
        return new Promise<Prefab>((resolve, reject) => {
            this.loadRes<Prefab>(`prefab/ui/${path}`,
                Prefab, (err: Error, res: Prefab) => {
                    if (err) {
                        error("UI模型加载失败：", err.message || err);
                        reject();
                        return;
                    }

                    resolve(res);
                }
            );
        });
    }

    /**
     * 加载精灵
     *
     * @param path 精灵路径
     */
    public static loadSpriteFrame(path: string): Promise<SpriteFrame> {
        return new Promise<SpriteFrame>((resolve, reject) => {
            this.loadRes<SpriteFrame>(`texture/${path}/spriteFrame`,
                SpriteFrame, (err: Error, res: SpriteFrame) => {
                    if (err) {
                        error("精灵加载失败：", err.message || err);
                        reject();
                        return;
                    }

                    resolve(res);
                }
            );
        });
    }

    /**
     * 加载JSON数据
     *
     * @param path JSON路径
     * @param onComplete 完成回调
     */
    public static loadJsonData(path: string, onComplete: (err: Error, data: Record<string, any>) => void) {
        this.loadRes<JsonAsset>(`datas/${path}`, null, (err: Error, res: JsonAsset) => {
            if (err) {
                error("JSON文件加载失败", err.message || err);
                onComplete(err, null);
                return;
            }

            if (res.json) {
                onComplete(err, res.json);
            } else {
                onComplete(new Error("loadJsonData failed!"), null);
            }
        });
    }

    /**
     * 加载Text数据
     *
     * @param path Text路径
     * @param onComplete 完成回调
     */
    public static loadTextData(path: string, onComplete: (err: Error, data: string) => void) {
        this.loadRes<TextAsset>(`datas/${path}`, null, (err: Error, res: TextAsset) => {
            if (err) {
                error("Text文件加载失败", err.message || err);
                onComplete(err, null);
                return;
            }

            if (res.text) {
                onComplete(err, res.text);
            } else {
                onComplete(new Error("loadTextData failed!"), null);
            }
        });
    }

    /**
     * 获取地图资源
     *
     * @param level 地图等级
     */
    public static getMap(level: number): Promise<any> {
        return new Promise<any>((resolve, reject) => {
            let levelStr: string = 'map';
            if (level >= 100) {
                levelStr += level;
            } else if (level >= 10) {
                levelStr += '0' + level;
            } else {
                levelStr += '00' + level;
            }

            this.loadRes<any>(`map/config/${levelStr}`,
                null, (err: Error, res: any) => {
                    if (err) {
                        error("精灵加载失败：", err.message || err);
                        reject();
                        return;
                    }

                    let content: string = '';
                    if (res._file) {
                        //@ts-ignore
                        if (window['LZString']) {
                            //@ts-ignore
                            content = window['LZString'].decompressFromEncodedURIComponent(txtAsset._file);
                        }
                        let objJson = JSON.parse(content);
                        resolve(objJson);
                    } else if (res.text) {
                        //@ts-ignore
                        if (window['LZString']) {
                            //@ts-ignore
                            content = window['LZString'].decompressFromEncodedURIComponent(txtAsset.text);
                        }
                        let objJson = JSON.parse(content);
                        resolve(objJson);
                    } else if (res.json) {
                        resolve(res.json);
                    } else {
                        resolve('failed');
                    }
                });
        });
    }

    /**
     * 加载关卡数据
     *
     * @param type 关卡类型
     * @param names 关卡名称
     * @param onProgress 过程回调
     * @param onComplete 完成回调
     */
    public static getMapObj(type: string, names: string[],
                            onProgress: (finished: number, total: number, item: AssetManager.RequestItem) => void,
                            onComplete: (err: Error, data: Prefab[]) => void) {
        let paths: string[] = [];
        for (let name of names) {
            paths.push(`map/${type}/${name}`);
        }
        resources.load(paths, Prefab, onProgress, onComplete);
    }

    /**
     * 创建UI，若不指定父节点，则父节点为Canvas
     *
     * @param path UI模型路径
     * @param onComplete 完成回调
     * @param parent 父节点
     */
    public static createUI(path: string, onComplete: (err: Error, data: Node) => void, parent?: Node) {
        this.loadRes<Prefab>(`prefab/ui/${path}`,
            Prefab, (err: Error, res: Prefab) => {
                if (err) {
                    error("UI模型加载失败：", err.message || err);
                    onComplete(err, null);
                    return;
                }
                // 创建节点
                let node = instantiate(res);
                // 设置位置
                node.setPosition(0, 0, 0);
                // 设置父节点
                if (!parent) {
                    parent = find("Canvas");
                }
                parent.addChild(node);
                // 完成回调
                onComplete(null, node);
            }
        );
    }

    /**
     * 设置精灵图
     *
     * @param path 精灵路径
     * @param sprite 精灵组件
     * @param callback 回调
     */
    public static setSpriteFrame(path: string, sprite: Sprite, callback?: (err: Error, data: SpriteFrame) => void) {
        this.loadRes<SpriteFrame>(`${path}/spriteFrame`, SpriteFrame, (err: Error, res: SpriteFrame) => {
            if (err) {
                error("精灵加载失败：", err.message || err);
                callback && callback(err, null);
                return;
            }

            if (sprite && isValid(sprite)) {
                sprite.spriteFrame = res;
                callback && callback(null, res);
            }
        });
    }
}

